﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using RandomGraph.Common.Model;
using RandomGraph.Common.Model.Generation;
using RandomGraph.Common.Model.Status;
using CommonLibrary.Model.Attributes;
using System.Threading;
using System.Collections;
using Algorithms;

namespace Model.ERModel
{
    [GraphModel("ERModel", GenerationRule.Sequential, "ERModel graph model")]
    [AvailableAnalyzeOptions(
        AnalyseOptions.DegreeDistribution | 
        AnalyseOptions.AveragePath |
        AnalyseOptions.Cycles3 | 
        AnalyseOptions.ClusteringCoefficient |
        AnalyseOptions.MinPathDist |
        AnalyseOptions.DistEigenPath |
        AnalyseOptions.EigenValue 
        )]
    [RequiredGenerationParam(GenerationParam.Vertices, 2)]
    [RequiredGenerationParam(GenerationParam.P, 3)]

    public class ERModel : AbstractGraphModel
    {
        private static readonly string MODEL_NAME = "ERModel";
        private ERGraph ERModelGraph;

        public ERModel() { }

        public ERModel(Dictionary<GenerationParam, object> genParam, AnalyseOptions options, int sequenceNumber)
            : base(genParam, options, sequenceNumber)
        {
            ValidateModelParams();
            InitModel();
        }

        private void ValidateModelParams()
        {
            //TODO Put input params validation here
            //and throw WrongModelParamsException

        }

        private void InitModel()
        {
            InvokeProgressEvent(GraphProgress.Initializing, 0);
            ModelName = MODEL_NAME;
            //Defines separate generation rule
            GenerationRule = GenerationRule.Sequential;

            //Defines available options for analizer
            AvailableOptions = AnalyseOptions.DegreeDistribution |
                               AnalyseOptions.AveragePath |
                               AnalyseOptions.Cycles3 |
                               AnalyseOptions.MinPathDist |
                               AnalyseOptions.DistEigenPath |
                               AnalyseOptions.EigenValue |
                               AnalyseOptions.ClusteringCoefficient;

            //Defines required input parameters for generation
            List<GenerationParam> genParams = new List<GenerationParam>();
            genParams.Add(GenerationParam.Vertices);
            genParams.Add(GenerationParam.P);
            RequiredGenerationParams = genParams;

            //Place additional initialization code here

            InvokeProgressEvent(GraphProgress.Ready);
        }


        protected override void GenerateModel()
        {
                InvokeProgressEvent(GraphProgress.StartingGeneration, 20);

                try
                {
                    if (Graph == null)
                    {
                            //Place generation initialization code here
                            ERModelGraph = new ERGraph((Int32)GenerationParamValues[GenerationParam.Vertices]);
                            Graph = ERModelGraph;
                    }
                    else
                    {
                        ERModelGraph = (ERGraph)Graph;//((BAGraph)Graph).Copy();
                        Graph = ERModelGraph;
                        System.Console.WriteLine("Copy PROCESS STARTED");
                    }
                    // InvokeProgressEvent(GraphProgress.Generating, 10);

                    ERModelGraph.Generate((double)GenerationParamValues[GenerationParam.P]);

                    //Place generating logic here
                    //Invoke ModelProgress event if possible to show current
                    //state with use of Percent and TargetItem properties

                    //Graph assignment is not needed for HEIRARCHIC(NOT NEEDED IF GENERATION 
                    //RULE IS SEPARATE) 
                    InvokeProgressEvent(GraphProgress.GenerationDone, 10);

                }
                catch (ThreadAbortException)
                {
                }

                catch (Exception)
                {
                    InvokeFailureProgressEvent(GraphProgress.GenerationFailed, "ENTER FAIL REASON HERE");
                    //RETHROW EXCEPTION 
                }
                finally
                {
                    //Place clean up code here
                }
        }

        protected override void AnalizeModel()
        {
            InvokeProgressEvent(GraphProgress.StartingAnalizing);

            try
            {
                //Get degree distrubtion

                // BAModelGraph.Analyze();

                if ((AnalizeOptions & AnalyseOptions.DegreeDistribution) == AnalyseOptions.DegreeDistribution)
                {
                    Result.Result[AnalyseOptions.DegreeDistribution] = ERModelGraph.avg_degree();
                    Result.VertexDegree = ERModelGraph.get_degree_dict();
                    InvokeProgressEvent(GraphProgress.Analizing, 32, "Degree distrubution");

                }
                //Get average path
               
                if ((AnalizeOptions & AnalyseOptions.AveragePath) == AnalyseOptions.AveragePath)
                {
                    InvokeProgressEvent(GraphProgress.Analizing, 39, "Average path distrubution");
                    Result.Result[AnalyseOptions.AveragePath] = ERModelGraph.avg_length();
                    InvokeProgressEvent(GraphProgress.Analizing, 50, "Average path distrubution");
                }

                if ((AnalizeOptions & AnalyseOptions.ClusteringCoefficient) == AnalyseOptions.ClusteringCoefficient)
                {
                    InvokeProgressEvent(GraphProgress.Analizing, 60, "Classtering Coefficient");
                    Result.Result[AnalyseOptions.ClusteringCoefficient] = ERModelGraph.clust_graph();
                    Result.Coefficient = ERModelGraph.get_coeff_dict();
                }

                if ((AnalizeOptions & AnalyseOptions.Cycles3) == AnalyseOptions.Cycles3)
                {
                    InvokeProgressEvent(GraphProgress.Analizing, 80, "Diameter");
                    Result.Result[AnalyseOptions.Cycles3] = ERModelGraph.cycle_3(3);
                }
                if ((AnalizeOptions & AnalyseOptions.EigenValue) == AnalyseOptions.EigenValue)
                {
                    InvokeProgressEvent(GraphProgress.Analizing, 90, "Calculating EigenValue");
                    Algorithms.EigenValue ev = new EigenValue();
                    bool[,] m = GetMatrix();
                    Result.EigenVector = ev.EV(m);
                    Result.DistancesBetweenEigenValues = ev.CalcEigenValuesDist();
                }
                InvokeProgressEvent(GraphProgress.AnalizingDone, 95);

            }
            catch (Exception ex)
            {
                InvokeFailureProgressEvent(GraphProgress.AnalizingFailed, "ENTER FAIL REASON HERE");
                //RETHROW EXCEPTION
            }
            finally
            {
                InvokeProgressEvent(GraphProgress.Done, 100);
            }
               
        }
        public override bool CheckGenerationParams(int instances)
        {
            System.Diagnostics.PerformanceCounter ramCounter = new System.Diagnostics.PerformanceCounter("Memory", "Available Bytes");
            UInt64 vertex = UInt64.Parse(GenerationParamValues[GenerationParam.Vertices].ToString());
            UInt64 vertexmemory = vertex * (vertex - 1) / 16;
            int processorcount = Environment.ProcessorCount;
            return vertexmemory < ramCounter.NextValue() / processorcount
                   && (int)GenerationParamValues[GenerationParam.Vertices] < 32000;
        }

        public override string GetParamsInfo()
        {
            return "";
        }

        public override bool[,] GetMatrix()
        {
            //throw new NotImplementedException();
            return ERModelGraph.to_adj_matrix_bool();
        }
    }
}
